﻿using System.Collections.Generic;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

namespace ShevaContentPipeline.Processors.AnimatedModels
{
    /// <summary>
    /// Mesh split part.
    /// </summary>
    internal class MeshSplitPart
    {
        /// <summary>Mesh splitter.</summary>
        private MeshSplitter _Splitter;
        /// <summary>Index.</summary>
        private int _Index;
        /// <summary>Vertex start index.</summary>
        private int _VertexStartIndex;
        /// <summary>Vertex end index.</summary>
        private int _VertexEndIndex;
        /// <summary>New mesh.</summary>
        private MeshContent _NewMesh;
        /// <summary>Old to new dictionary.</summary>
        private SortedDictionary<int, int> _OldToNewDict;
        /// <summary>New to old dictionary.</summary>
        private SortedDictionary<int, int> _NewToOldDict;
        /// <summary>Weight chanel.</summary>
        private VertexChannel<BoneWeightCollection> _WeightChannel;
        /// <summary>Geometry.</summary>
        private GeometryContent _Geom;
        /// <summary>Mesh bones.</summary>
        private SortedDictionary<string, object> _MeshBones;

        /// <summary>
        /// Index.
        /// </summary>
        public int Index
        {
            get { return _Index; }
        }

        /// <summary>
        /// Vertex start index.
        /// </summary>
        public int VertexStartIndex
        {
            get { return _VertexStartIndex; }
        }

        /// <summary>
        /// Vertex end index.
        /// </summary>
        public int VertexEndIndex
        {
            get { return _VertexEndIndex; }
        }

        /// <summary>
        /// Konstruktor.
        /// </summary>
        /// <param name="splitter">Mesh splitter object.</param>
        /// <param name="geom">Geometry.</param>
        /// <param name="vertexStartIndex">Vertex start index.</param>
        public MeshSplitPart(
            MeshSplitter splitter,
            GeometryContent geom,
            int vertexStartIndex)
        {
            this._Index = vertexStartIndex;
            this._Splitter = splitter;

            this._OldToNewDict = new SortedDictionary<int, int>();
            this._NewToOldDict = new SortedDictionary<int, int>();
            this._MeshBones = new SortedDictionary<string, object>();
            this._VertexStartIndex = vertexStartIndex;
            this._VertexEndIndex = vertexStartIndex;
            this._Geom = geom;
            _GetWeightChannel();
        }

        /// <summary>
        /// Process method.
        /// </summary>
        /// <returns></returns>
        public MeshContent Process()
        {
            _NewMesh = new MeshContent();
            _NewMesh.Name = _Splitter._Mesh.Name + _Splitter._CurrentIndex.ToString();
            SortedDictionary<string, object> faceBones = new SortedDictionary<string, object>();
            GeometryContent newGeom = new GeometryContent();
            while (_Index < _Geom.Indices.Count - 1)
            {
                int[] faceIndices = new int[]
                        {
                            _Geom.Indices[_Index],
                            _Geom.Indices[_Index+1],
                            _Geom.Indices[_Index+2]
                        };

                for (int i = 0; i < 3; i++)
                {
                    BoneWeightCollection weightCollection = _WeightChannel[
                        _Geom.Indices[_Index + i]];
                    foreach (BoneWeight weight in weightCollection)
                    {
                        if (!_MeshBones.ContainsKey(weight.BoneName) &&
                            !faceBones.ContainsKey(weight.BoneName))
                            faceBones.Add(weight.BoneName, null);
                    }
                }
                if (_MeshBones.Count + faceBones.Count > _Splitter._MaxBones)
                {
                    faceBones.Clear();
                    _VertexEndIndex = _Index;
                    break;
                }

                foreach (string s in faceBones.Keys)
                    _MeshBones.Add(s, null);
                faceBones.Clear();
                for (int i = 0; i < 3; i++)
                {
                    if (_OldToNewDict.ContainsKey(faceIndices[i]))
                    {

                    }
                    else
                    {
                        int newIndex = _NewMesh.Positions.Count;
                        _NewMesh.Positions.Add(_Geom.Vertices.Positions[faceIndices[i]]);

                        _OldToNewDict.Add(faceIndices[i], newIndex);
                        newGeom.Vertices.Add(newIndex);
                    }
                    newGeom.Indices.Add(_OldToNewDict[faceIndices[i]]);

                }
                _Index += 3;
                _VertexEndIndex = _Index;
            }
            _NewMesh.Geometry.Add(newGeom);
            _Finish();
            return _NewMesh;
        }

        /// <summary>
        /// Finish method.
        /// </summary>
        private void _Finish()
        {
            foreach (KeyValuePair<int, int> k in _OldToNewDict)
            {
                _NewToOldDict.Add(k.Value, k.Key);
            }

            foreach (GeometryContent newGeom in _NewMesh.Geometry)
            {
                foreach (VertexChannel channel in _Geom.Vertices.Channels)
                {
                    List<object> data = new List<object>();
                    foreach (int i in newGeom.Vertices.PositionIndices)
                    {
                        data.Add(channel[_NewToOldDict[i]]);
                    }
                    newGeom.Vertices.Channels.Add(
                        channel.Name, channel.ElementType, data);
                }
                newGeom.Material = _Geom.Material;
                foreach (KeyValuePair<string, object> k in _Geom.OpaqueData)
                {
                    if (!newGeom.OpaqueData.ContainsKey(k.Key))
                        newGeom.OpaqueData.Add(k.Key, k.Value);
                }
            }
        }

        /// <summary>
        /// Method gets weight channel.
        /// </summary>
        private void _GetWeightChannel()
        {
            foreach (VertexChannel channel in _Geom.Vertices.Channels)
            {
                if (channel.Name == VertexChannelNames.Weights())
                {
                    _WeightChannel = (VertexChannel<BoneWeightCollection>)channel;
                    break;
                }
            }
        }
    }
}
